package com.spx.chapter06;

import com.spx.chapter05.pojo.Event;
import com.spx.chapter06.entity.UrlViewCount;
import org.apache.flink.api.common.eventtime.SerializableTimestampAssigner;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.api.common.functions.AggregateFunction;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.windowing.ProcessWindowFunction;
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;
import org.apache.flink.util.OutputTag;

import java.time.Duration;

/**
 * create by undeRdoG on  2022-05-03  14:06
 * 凡心所向，素履以往，生如逆旅，一苇以航。
 */
public class LateDataTest {


    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        env.setParallelism(1);

        DataStreamSource<String> socketStream = env.socketTextStream("hadoop102", 7777);

        SingleOutputStreamOperator<Event> mapSource = socketStream.map(new MapFunction<String, Event>() {
            @Override
            public Event map(String value) throws Exception {
                String[] fields = value.split(",");
                return new Event(fields[0].trim(), fields[1].trim(), Long.parseLong(fields[2].trim()));
            }
        });

        // 设置watermark
        SingleOutputStreamOperator<Event> dataSource = mapSource.assignTimestampsAndWatermarks(
                // 设置2s的watermark
                WatermarkStrategy.<Event>forBoundedOutOfOrderness(Duration.ofSeconds(2L))
                        .withTimestampAssigner(new SerializableTimestampAssigner<Event>() {
                            @Override
                            public long extractTimestamp(Event element, long recordTimestamp) {
                                return element.timestamp;
                            }
                        })
        );

        OutputTag<Event> outputTag = new OutputTag<Event>("late") {
        };

        SingleOutputStreamOperator<UrlViewCount> result = dataSource.keyBy(data -> data.url)
                // 开一个10s的滚动事件时间窗口
                .window(TumblingEventTimeWindows.of(Time.seconds(10L)))
                // 设置AllowedLateness为1分钟
                .allowedLateness(Time.minutes(1L))
                .sideOutputLateData(outputTag)
                .aggregate(new CustomAggregateFunction(), new CustomProcessWindowFunction());


        mapSource.print("input");

        result.print("result");

        result.getSideOutput(outputTag).print("late");

        env.execute();


    }


    public static class CustomAggregateFunction implements AggregateFunction<Event, Tuple2<String, Long>, UrlViewCount> {


        @Override
        public Tuple2<String, Long> createAccumulator() {
            return Tuple2.of(null, 0L);
        }

        @Override
        public Tuple2<String, Long> add(Event value, Tuple2<String, Long> accumulator) {
            accumulator.setFields(value.url, accumulator.f1 + 1);
            return accumulator;
        }

        @Override
        public UrlViewCount getResult(Tuple2<String, Long> accumulator) {
            UrlViewCount urlViewCount = new UrlViewCount();
            urlViewCount.url = accumulator.f0;
            urlViewCount.count = accumulator.f1;
            return urlViewCount;
        }

        @Override
        public Tuple2<String, Long> merge(Tuple2<String, Long> a, Tuple2<String, Long> b) {
            return null;
        }
    }



    public static class CustomProcessWindowFunction extends ProcessWindowFunction<UrlViewCount,UrlViewCount,String, TimeWindow>{

        @Override
        public void process(String s, Context context, Iterable<UrlViewCount> elements, Collector<UrlViewCount> out) throws Exception {
            UrlViewCount urlViewCount = elements.iterator().next();
            urlViewCount.start = context.window().getStart();
            urlViewCount.end = context.window().getEnd();
            out.collect(urlViewCount);
        }
    }
}
